home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 August: Tool Chest / Dev.CD Aug 00 TC Disk 2.toast / pc / sample code / human interface toolbox / hiermenus / hiermenus.p < prev    next >
Encoding:
Text File  |  2000-06-23  |  11.1 KB  |  322 lines

  1. {
  2.     File:        HierMenus.p
  3.  
  4.     Contains:   HierMenus is a very simple demonstration of how to use 
  5.                 hierarchical menus in your application.
  6.  
  7.                 We recommend that you review Sample or TESample for the general 
  8.                 structure and MultiFinder techniques you should use when writing a 
  9.                 new application.
  10.                 
  11.                 Hierarchical Menu Example
  12.  
  13.                 This program is a simple example of how to add hierarchical menus to your
  14.                 application. A few tips:
  15.  
  16.                 The toughest part is defining the menu item that "owns" the submenu. The
  17.                 item must be defined with a command-key of $1B, and a "mark" character
  18.                 whose value is the ID of the submenu. The submenu is defined just like
  19.                 a normal menu, but is inserted in the Menu Manager's private "hierarchical"
  20.                 menu list by specifying -1 as the second parameter to InsertMenu. 
  21.     
  22.                 It's easiest to declare all your menus (and submenus) as resources (as
  23.                 opposed to creating them at runtime with NewMenu), even if you're creating
  24.                 a submenu that will initially contain no items (like the Font submenu in 
  25.                 this example). This simplifies your program's handling of submenus (note
  26.                 that in this example, since the submenus' IDs follow the regular menu's
  27.                 ID, the only special handling of submenus is the test (in SetUpMenus) to
  28.                 decide whether to use 0 or -1 as the second parameter to InsertMenu!).
  29.  
  30.                 Submenus owned by applications' menus' IDs should always be in the range
  31.                 1 through 235 (inclusive); desk accessories should use IDs 236 through
  32.                 255, and should leave their menus and submenus in the menu bar only when
  33.                 they're active (that is, call InsertMenu when handling an activate event,
  34.                 and DeleteMenu when handling a deactivate event). This really applies to 
  35.                 all menus; it's most important for pop-up and submenus, however. All this
  36.                 to prevent conflicts (it may be helpful to note that in searching through 
  37.                 the menu list, the hierarchical list [containing all submenus & pop-up
  38.                 menus] is searched first).
  39.  
  40.                 This particular example is meant to show the mechanics of creating and
  41.                 handling events from submenus; in the interest of simplicity, certain nice
  42.                 features (like checkmarking the current font and styles, setting the 
  43.                 style of "real" font sizes in the Font submenu to "outline", etc) have been
  44.                 omitted.
  45.  
  46.                 Have fun.
  47.  
  48.                 Note: the current version does not actually call SysEnvirons to
  49.                 determine whether System 4.1 is running. This will be fixed, but
  50.                 in the meantime, run this with System 
  51.  
  52.     Written by: Bryan Stearns    
  53.  
  54.     Copyright:    Copyright © 1987-1999 by Apple Computer, Inc., All Rights Reserved.
  55.  
  56.                 You may incorporate this Apple sample source code into your program(s) without
  57.                 restriction. This Apple sample source code has been provided "AS IS" and the
  58.                 responsibility for its operation is yours. You are not permitted to redistribute
  59.                 this Apple sample source code as "Apple sample source code" after having made
  60.                 changes. If you're going to re-distribute the source, we require that you make
  61.                 it clear in the source that the code was descended from Apple sample source
  62.                 code, but that you've made changes.
  63.  
  64.     Change History (most recent first):
  65.                 8/6/1999    Karl Groethe    Updated for Metrowerks Codewarror Pro 2.1
  66.                 
  67.  
  68. }
  69. PROGRAM HierMenus;
  70.  
  71. USES Types,TextUtils,Menus,ToolUtils,Fonts,Windows,TextEdit,Dialogs;
  72.  
  73. {$R-} {no range checking}
  74. {$D+} {Generate debug symbols}
  75.  
  76. CONST
  77.     menuBase = 128;            {we number our menus starting at 128}
  78.     
  79.     mainMenu = menuBase;    {main menu ID}
  80.         {we don't care about the Font/Style/Size item numbers; we}
  81.         {deal with choices in the submenus directly}
  82.         quitItem = 5;            {Quit item number}
  83.     
  84.     {the Font submenu ID}
  85.     fontMenu = mainMenu+1;        
  86.         {we retrieve the font name from the menu by item number,}
  87.         {then use GetFNum(the font name) to convert to family}
  88.         {number. The font Name should be stored with the document,}
  89.         {not the family number (because Font/DA Mover renumbers)}
  90.         
  91.     {the Style submenu ID}
  92.     styleMenu = fontMenu+1;        
  93.         {Styles are dealt with in the same order they appear in}
  94.         {the Quickdraw "Style" type. Sneaky, but consistent with}
  95.         {other Macintosh applications}
  96.         
  97.     {the Size submenu ID}
  98.     sizeMenu = styleMenu+1;         {the Size submenu ID}
  99.         {Sizes are retrieved as text from the menu item; this would}
  100.         {allows advanced users to edit the menu to add custom sizes}
  101.         
  102.     lastMenu = sizeMenu;        {the last menu resource}
  103.     firstSubMenu = fontMenu;     {the first submenu - see SetUpMenus}
  104.     
  105.     myWINDid = 128;            {our window template}
  106.     myALRTid = 128;         {our “need right machine & sys software” alert}
  107.     mySTRListID = 128;        {our string list}
  108.     
  109. VAR    myWindow: WindowPtr;        {our window pointer}
  110.     myEvent: EventRecord;        {Event record for GetNextEvent}    
  111.     myMenus: ARRAY [menuBase..lastMenu] OF MenuHandle; {our menus}
  112.     done: BOOLEAN;                {a flag that the user chose Quit}
  113.     
  114.     demoStr1, demoStr2,            {Strings to draw in our demo window}
  115.     demoStr3, demoStr4: Str255;    
  116.     
  117.     curFont: INTEGER;            {the current font number}
  118.     curSize: INTEGER;
  119.     curStyle: Style;
  120.     
  121.     i: INTEGER;                    {a temp}
  122.     
  123.     
  124. {*
  125.  * Once-only initialization for my menus
  126.  * (Thanks, Andy!)
  127.  *}
  128. PROCEDURE SetUpMenus;
  129. VAR i: INTEGER;
  130. BEGIN
  131.     {Get and insert each menu. The normal menus are inserted using zero}
  132.     {as the second parameter to InsertMenu; the submenus get inserted using}
  133.     {-1, to tell the Menu Manager to insert in the hierarchical menu list}
  134.     FOR i := menuBase TO lastMenu DO BEGIN
  135.         myMenus[i] := GetMenu(i); {get the menu from the resource}
  136.         
  137.         {insert it into a menu bar, one way or another}
  138.         IF i < firstSubMenu THEN
  139.             {it's a regular menu-bar menu}
  140.             InsertMenu(myMenus[i],0) {insert at end of normal menu list}
  141.         ELSE 
  142.             {it's a submenu}
  143.             InsertMenu(myMenus[i],-1); {insert hierarchical submenu}
  144.     END; {for each menu}
  145.     
  146.     {Add the fonts to the Font submenu}
  147.     AppendResMenu(myMenus[fontMenu],'FONT'); {add fonts to the font menu}
  148.     
  149.     DrawMenuBar; {draw our menus' titles}
  150. END; {setupmenus}
  151.  
  152.  
  153. {*
  154.  * Handle a command, either from a command key or from
  155.  * a menu choice. MResult is the value returned by 
  156.  * MenuSelect or MenuKey.
  157.  *}
  158. PROCEDURE DoCommand (mResult: LONGINT);
  159. VAR theItem,theMenu: INTEGER;
  160.     tmpStr: Str255;
  161.     tmpLong: LongInt;
  162. BEGIN
  163.     theItem := LoWord(mResult); {extract the menu and item numbers}
  164.     theMenu := HiWord(mResult);    
  165.     CASE theMenu OF {which menu?}
  166.         mainMenu:  CASE theItem OF
  167.             {Note that we don't have any cases for the font, style, or}
  168.             {size menus here: those are handled independently, below. Only}
  169.             {normal items that appear in this menu are taken care of here.}
  170.             quitItem: done := TRUE; 
  171.         END; {mainMenu case}
  172.  
  173.         fontMenu: BEGIN
  174.             {Here, we just change a global font number. However, if your}
  175.             {application saves font information in its documents, you should}
  176.             {save the font name (rather than the number), to avoid remapping}
  177.             {because of Font/DA Mover number changes. Each time you open}
  178.             {such a document, use GetFNum to find the right "current number".}
  179.             GetMenuItemText(myMenus[fontMenu],theItem,tmpStr); {get the chosen font’s name}
  180.             GetFNum(tmpStr,curFont); {get the new font’s number, to our global}
  181.             InvalRect(qd.thePort^.portRect); {remember that we need to redraw}
  182.         END; {fontMenu case}
  183.  
  184.         styleMenu: BEGIN
  185.             {The styles (other than Plain) are arranged in the}
  186.             {menu in the same order that they appear in the}
  187.             {Style type. If it's not plain, we can convert the}
  188.             {item number to the proper enumerated value for the style}
  189.             {and use set math to toggle the particular style}
  190.             {characteristic.}
  191.             IF theItem = 1 THEN curStyle := [] ELSE BEGIN {not 'Plain'}
  192.                 {it's not plain, so toggle this style characteristic}
  193.                 IF StyleItem(theItem-2) IN curStyle THEN
  194.                     {this style characteristic is currently on, so turn it off}
  195.                     curStyle := curStyle - [StyleItem(theItem-2)]
  196.                 ELSE
  197.                     {this style characteristic is currently on, so turn it off}
  198.                     curStyle := curStyle + [StyleItem(theItem-2)];
  199.             END;
  200.             InvalRect(qd.thePort^.portRect); {remember that we need to redraw}
  201.         END; {styleMenu case}
  202.  
  203.         sizeMenu: BEGIN
  204.             {Read the size out of the menu item}
  205.             GetMenuItemText(myMenus[sizeMenu],theItem,tmpStr); {get the chosen string}
  206.             StringToNum(tmpStr,tmpLong); {convert to longint}
  207.             curSize := tmpLong; {convert it to integer}
  208.             InvalRect(qd.thePort^.portRect); {remember that we need to redraw}
  209.         END; {sizeMenu case}
  210.         
  211.       END; {menu case}          
  212.     IF NOT done THEN HiliteMenu(0); {un-hilight the chosen menu item}
  213. END; {DoCommand}
  214.  
  215.  
  216. {*
  217.  * Main event loop
  218.  *}
  219. PROCEDURE MainEventLoop;
  220. VAR thePart, theHeight: INTEGER;
  221.     whichWindow: WindowPtr;
  222.     theChar: CHAR;
  223.     dragRect: Rect;
  224.     fInfo: FontInfo;
  225. BEGIN
  226.     done := FALSE; {we're not through yet!}
  227.     dragRect := qd.screenBits.bounds;
  228.     
  229.     {Main event loop}
  230.     REPEAT  
  231.         SystemTask; {let the desk accs run}
  232.         IF GetNextEvent(everyEvent,myEvent) THEN WITH myEvent DO BEGIN
  233.             CASE what OF 
  234.                 mouseDown: BEGIN {"Click"}
  235.                     thePart := FindWindow(where,whichWindow);
  236.                     CASE thePart OF
  237.                         inSysWindow: SystemClick(myEvent,whichWindow);
  238.                         inMenuBar: DoCommand(MenuSelect(myEvent.where)); {nothing special here!}
  239.                         inDrag:    DragWindow(whichWindow,myEvent.where,dragRect);
  240.                     END; {case}
  241.                 END; {mouseDown}
  242.  
  243.                 keyDown, autoKey: BEGIN
  244.                     theChar := CHR(BitAnd(myEvent.message,charCodeMask));  {get the char}
  245.                     IF BitAnd(myEvent.modifiers,cmdKey) <> 0 THEN BEGIN
  246.                         DoCommand(MenuKey(theChar)); {pass it to the command handler}
  247.                     END ELSE BEGIN {not a command key}
  248.                         {ignore it}
  249.                     END; {if not command key}
  250.                 END; {keydown}
  251.  
  252.                 updateEvt: BEGIN {our window needs drawing}
  253.                     BeginUpdate(Windowptr(message));
  254.                     
  255.                     SetPort(myWindow); {make sure we're in our port}
  256.                     EraseRect(qd.thePort^.portRect); {erase the old stuff}
  257.                     
  258.                     TextFont(curFont); {Set the right font/size/style}
  259.                     TextSize(curSize);
  260.                     TextFace(curStyle);
  261.                     
  262.                     GetFontInfo(fInfo);
  263.                     WITH fInfo DO theHeight := ascent+descent+leading;
  264.                     MoveTo(20,20+theHeight); DrawString(demoStr1);
  265.                     MoveTo(20,20+(theHeight*2)); DrawString(demoStr2);
  266.                     MoveTo(20,20+(theHeight*3)); DrawString(demoStr3);
  267.                     MoveTo(20,20+(theHeight*5)); DrawString(demoStr4);
  268.                                         
  269.                     EndUpdate(WindowPtr(message));
  270.                 END;
  271.             END; {case}
  272.         END; {if we got an event}
  273.         
  274.     UNTIL done; {keep it up until the user quits}
  275.     
  276. END; {maineventloop}
  277.  
  278.  
  279. {*
  280.  * Main
  281.  *}
  282. BEGIN
  283.     {Initialize all the usual managers}
  284.     InitGraf(@qd.thePort);    
  285.     InitFonts;            
  286.     FlushEvents(everyEvent,0);
  287.     InitWindows;                
  288.     InitMenus;            
  289.     TEInit;
  290.     InitDialogs(NIL);            
  291.     InitCursor;            
  292.     
  293.     {Check to make sure we’re running on a machine}
  294.     {capable of supporting hierarchical menus; clearly,}
  295.     {we must do this before calling SetUpMenus!}
  296.     (** SysEnvirons(xxx); **)
  297.     IF FALSE THEN BEGIN {Sorry, see your dealer}
  298.         i := StopAlert(myALRTid,NIL); {put up the alert}
  299.         ExitToShell; {back to the finder}
  300.     END;
  301.     
  302.     curFont := 1; {Application font}
  303.     curSize := 0; {default size}
  304.     curStyle := []; {plain}
  305.     
  306.     SetUpMenus; {the oldest Macintosh subroutine!}
  307.     
  308.     {read the strings that we draw in our window}
  309.     {from a STR# resource}
  310.     GetIndString(demoStr1,mySTRListID,1);
  311.     GetIndString(demoStr2,mySTRListID,2);
  312.     GetIndString(demoStr3,mySTRListID,3);
  313.     GetIndString(demoStr4,mySTRListID,4);
  314.     
  315.     {Get our window}
  316.     myWindow := GetNewWindow(myWINDid,NIL,POINTER(-1));
  317.  
  318.     MainEventLoop; {handle events until the user quits}
  319. END.
  320.  
  321.  
  322.